package mc

import (
	"fmt"
	"gitee.com/benben-mao/djson"
	"github.com/spf13/cast"
	"gorm.io/gorm"
	"reflect"
	"strings"
)

// 默认kv项的名称（KEY)
const DefaultKvItemName string = "default"

type Model struct {
	config    *modelConfig
	db        *gorm.DB
	ctx       *Context
	name      string
	isRowAuth bool
}

type ModelOption struct {
	Name string
	Value string
}

// 字段外联来源信息
type FromInfo struct {
	IsKv       bool   // 是否kv
	FromName   string // 来源 (外链表对应model name, Enum对应enum name)
	KvItemName string // 如果是kv，对应的就是kvs配置项的单项key name
	IsRowAuth  bool   // 此字段是否受行权限控制
}

// 来源数据信息
type FromData struct {
	List map[string]map[string]interface{}
	Sort []string
}

func NewFromData() *FromData {
	return &FromData{List:make(map[string]map[string]interface{}), Sort:make([]string,0)}
}

//  新建一个自定义配制模型
//  @param modelName  模型名
func NewModel(ctx *Context, modelName string, options ...ModelOption) (m *Model) {
	opt := make(map[string]string)
	for _, v := range options {
		opt[v.Name] = v.Value
	}
	config := &modelConfig{}
	configContent, scripts := option.GetModelConfigCallback(ctx, modelName, opt)
	if reflect.TypeOf(configContent).Kind() == reflect.String && !strings.Contains(configContent.(string), "{") {
		if err := djson.FileUnmarshal(configContent.(string), config, scripts); err != nil {
			panic(ErrorInternalServerError.Copy("解析模型配置出错：%s", err.Error()))
		}
	} else {
		if err := djson.Unmarshal(configContent, config, scripts); err != nil {
			panic(ErrorInternalServerError.Copy("解析模型配置出错：%s", err.Error()))
		}
	}

	config.parse(modelName)
	m = &Model{
		name:      modelName,
		config:    config,
		ctx:       ctx,
		isRowAuth: InArray(modelName, option.ModelAuth.GetRowAuthModelsCallback()),
	}

	//创建一个连接并附加模型基础条件信息
	m.db = m.BaseDB(true)
	if config.Table.Where != nil {
		m.db.Where(config.Table.Where[0], config.Table.Where[1:]...)
	}
	if config.Table.Joins != nil || len(config.Table.Joins) > 0 {
		m.db.Joins(strings.Join(config.Table.Joins, " "))
	}
	if config.Table.Groups != nil || len(config.Table.Groups) > 0 {
		m.db.Group(strings.Join(m.FieldsAddAlias(config.Table.Groups), ","))
	}
	return
}

// 模型名称
func (m *Model) Name() string {
	return m.name
}

// 模型本身是否行权限模型
func (m *Model) IsRowAuth() bool {
	return m.isRowAuth
}

// 模型是否是树形结构
func (m *Model) IsTree() bool {
	return m.config.IsTree()
}

// 模型树形结构信息
func (m *Model) Tree() (tree *treeConfig) {
	return m.config.Tree
}

// 模型表信息
func (m *Model) Table() (table tableConfig) {
	return m.config.Table
}

// 模型扩展信息
func (m *Model) Extra() (extra map[string]interface{}) {
	extra = m.config.Extra
	return
}
// 基础字段信息
func (m *Model) Field(fieldName string) *fieldConfig {
	return m.config.Fields[m.config.fieldIndexes[fieldName]]
}
func (m *Model) Fields() []*fieldConfig {
	return m.config.Fields
}

// 模型行权限字段集
func (m *Model) RowAuthFields() []string {
	return m.config.rowAuthFields
}

// 获取字段来源数据
// @param fromInfo 来源信息
// @return fromData 来源数据
func (m *Model) GetFromData(fromInfo *FromInfo) (fromData *FromData) {

	if fromInfo == nil {
		return
	}
	if fromInfo.IsKv {
		var newM *Model
		if fromInfo.FromName == m.name || fromInfo.FromName == "" {
			newM = m
		} else {
			newM = NewModel(m.ctx, fromInfo.FromName)
		}
		fromData, _ = newM.QueryKv(fromInfo.KvItemName)
	} else {
		enum, _ := m.Enum(fromInfo.FromName)
		fromData = m.GetFromDataByEnum(enum)
	}
	return
}

func (m *Model) GetFromDataByEnum(enum [][]string) (fromData *FromData)  {
	fromData = NewFromData()
	for _, v := range enum {
		fromData.List[v[0]] = map[string]interface{}{
			"__mc_key":   v[0],
			"__mc_value": v[1],
		}
		for index, extra := range v[2:] {
			key := fmt.Sprintf("extra_%d", index)
			fromData.List[v[0]][key] = extra
		}
		fromData.Sort = append(fromData.Sort, v[0])
	}
	return
}

func (m *Model) Enum(key string) (enum [][]string, ok bool) {
	enum, ok = m.config.Enums[key]
	if !ok {
		enum = option.CommonEnums[key]
	}
	return
}

// 获取一个页面元素集
// @param key 页面关键字
// @param useColAUth 是否使用列权限
// @param values 各字段等绑定的值
// @param useFromData
// @param createWidget
// @return page 页面元素集
func (m *Model) GetPage(key string, useColAuth bool) (page *ModelPage) {
	// 新建一个页面元素集
	page = &ModelPage{
		Model:  m,
		Name:   key,
		Items:  make([]string, 0),
		Fields: make(map[string]*ModelField),
		//Data:   make(map[string]interface{}),
	}
	// 获取此页面配置信息
	cfgPage, ok := m.config.Pages[key]
	if !ok {
		return page
	}
	// 页面禁用
	if cfgPage.Disabled {
		panic(ErrorBadRequest.Copy("此项操作已被禁用").Info())
	}
	// 分析页面配置信息
	cfgPage.parse(m, key)
	page.List = cfgPage.List
	page.Javascript = cfgPage.Javascript
	page.Extra = cfgPage.Extra
	page.UseAction = cfgPage.UseAction
	page.SearchHidden = cfgPage.SearchHidden
	// 列权限
	colAuth, isAllAuth := option.ModelAuth.GetColAuthCallback(m.ctx, m.name)
	for _, fieldName := range cfgPage.Items {
		//排除的字段
		if cfgPage.ExcludeItems != nil && InArray(fieldName, cfgPage.ExcludeItems) {
			continue
		}
		if useColAuth {
			// 分析列权限 (colAuth中存在的，表示没有权限)
			if isAllAuth || !InArray(fieldName, colAuth) {
				page.Items = append(page.Items, fieldName)
			}
		} else {
			page.Items = append(page.Items, fieldName)
		}
	}
	for _, fieldName := range page.Items {
		field := ModelField{
			fieldConfig: cfgPage.Fields[cfgPage.fieldIndexes[fieldName]],
		}
		// 分析数据来源
		if field.From != "" && field.FromInfo == nil {
			field.FromInfo = ParseFromInfo(field.From)
		}
		page.Fields[fieldName] = &field
	}

	if cfgPage.SearchPageName != "" {
		// 此处useFromData保持与createWidget同步
		page.SearchPage = m.GetPage(cfgPage.SearchPageName, false)
	}
	return
}

// 获取模型数据库连接对象本身
// 对此修改会影响模型本身的数据库连接
func (m *Model) DB() *gorm.DB {
	return m.db
}

// 获取一个新的模型数据库连接对象
// 对此修改不会影响模型本身的数据库连接
func (m *Model) NewDB() *gorm.DB {
	return m.db.Session(&gorm.Session{}).Where("")
}

// 获取一个仅包含连接名及表名的连接对象
// @param isAs 表是否带别名
func (m *Model) BaseDB(isAs bool) *gorm.DB {
	db := GetDB(m.config.Table.ConnName)
	if isAs {
		tb := fmt.Sprintf("%s AS %s", m.config.Table.Name, m.config.Table.Alias)
		if m.config.Table.DBName != "" {
			tb = fmt.Sprintf("`%s`.%s", m.config.Table.DBName, tb)
		}
		db.Table(tb)
	} else {
		db.Table(m.config.Table.Name)
	}
	return db
}

// 获取Kv键值列表
// @param kvName kv配置名
// @return dest 查询结果
func (m *Model) QueryKv(kvName string) (dest *FromData, err error){
	return m.QueryKvByWhere(kvName, nil)
}

// 根据条件获取Kv键值列表
// @param kvName kv配置名
// @param query 查询条件
// @param arg 查询条件值
// @return dest 查询结果
func (m *Model) QueryKvByWhere(kvName string, query interface{}, arg ...interface{}) (dest *FromData, err error) {
	// 检查选项
	if kvName == "" {
		kvName = DefaultKvItemName
	}
	kvCfg, ok := m.config.Kvs[kvName]
	if !ok {
		err = fmt.Errorf("配置中不存在 [%s] kv 项配置", kvName)
		return
	}
	if kvCfg.Joins != nil || len(kvCfg.Joins) > 0 {
		m.db.Joins(strings.Join(kvCfg.Joins, " "))
	}
	// 分析字段
	fields := make([]string, 0)
	fields = append(fields, kvCfg.KeyFields...)
	fields = append(fields, kvCfg.ValueFields...)
	for i, _ := range fields {
		fields[i] = strings.ReplaceAll(fields[i], "__mc_", "")
	}
	fields = RemoveRepeatedElement(fields)

	// 新建一个页面元素集
	page := &ModelPage{
		Model:  m,
		Name:   kvName,
		Items:  fields,
		Fields: make(map[string]*ModelField),
		//Data:   make(map[string]interface{}),
	}
	for _, fieldCfg := range m.config.Fields {
		field := ModelField{
			fieldConfig: fieldCfg,
		}
		// 分析数据来源
		if field.From != "" && field.FromInfo == nil {
			field.FromInfo = ParseFromInfo(field.From)
		}
		page.Fields[field.Name] = &field
	}

	var where []interface{}

	if kvCfg.Where != "" || query != nil {
		where = make([]interface{},0)
		if kvCfg.Where != "" {
			where = append(where, kvCfg.Where)
		}
		if query != nil {
			if len(where) > 0 {
				where[0] = fmt.Sprintf("(%v) AND (%v)", where[0], query)
			} else {
				where = append(where, query)
			}
			where = append(where, arg...)
		}
	}

	qo := &QueryOption{
		ExtraWhere:          where,
		ExtraFields:         kvCfg.ExtraFields,
		Order:               kvCfg.Order,
		PageSize:            -1,
		NotTotal:            true,
		NotSearch:           true,
		NotCount:            true,
		TreeIndent:          kvCfg.TreeIndent,
		AttachFromRealValue: true,
	}
	// 查询
	data := make([]map[string]interface{}, 0)
	data, _, _, err = page.Query(qo)

	// 处理结果
	dest = NewFromData()
	for _, v := range data {
		keys := make([]interface{}, 0)
		values := make([]interface{}, 0)
		for _, fieldName := range kvCfg.KeyFields {
			if !InArray(fieldName, kvCfg.NullFilterFields)  || !IsEmpty(v[fieldName]){
				keys = append(keys, v[fieldName])
			}
		}
		for _, fieldName := range kvCfg.ValueFields {
			if !InArray(fieldName, kvCfg.NullFilterFields)  || !IsEmpty(v[fieldName]){
				values = append(values, v[fieldName])
			}
		}
		key := Join(keys, kvCfg.KeyConnector)
		value := Join(values, kvCfg.ValueConnector)
		if m.config.IsTree() && kvCfg.TreeIndent != "" { // 树形名称字段加前缀
			_ = RepeatString(kvCfg.TreeIndent, cast.ToInt(v["__mc_level"])-1) + cast.ToString(v["__mc_value"])
		}
		v["__mc_key"] = key
		v["__mc_value"] = value
		dest.List[key] = v
		dest.Sort = append(dest.Sort, key)
	}
	return
}

// 根据PK字段删除记录
func (m *Model) DeleteById(id interface{}) (rowsAffected int64, err error) {
	var delIds interface{}
	kind := reflect.TypeOf(id).Kind()
	symbol := ""
	if kind == reflect.Array || kind == reflect.Slice {
		symbol = "IN"
		delIds = id
	} else {
		symbol = "="
		delIds = []interface{}{id}
	}
	db := m.BaseDB(false).Where(fmt.Sprintf("`%s` %s ?", m.config.Table.Pk, symbol), delIds).Delete(nil)
	return db.RowsAffected, db.Error
}

// 根据自定义条件删除记录
// @param query 删除条件
// @param args 条件值
func (m *Model) Delete(query interface{}, args ...interface{}) (rowsAffected int64, err error) {
	db := m.BaseDB(false).Where(query, args...).Delete(nil)
	return db.RowsAffected, db.Error
}

// 分析行权限查询条件
func (m *Model) ParseRowAuthWhere(db *gorm.DB) {
	if m.ctx == nil {
		return
	}
	// 受行权限控制的字段
	for _, fieldName := range m.config.rowAuthFields {
		field := m.config.Fields[m.config.fieldIndexes[fieldName]]
		if field.FromInfo != nil && field.FromInfo.IsRowAuth {
			if rowAuth, isAllAuth := option.ModelAuth.GetRowAuthCallback(m.ctx, field.FromInfo.FromName); !isAllAuth {
				asFieldName := m.FieldAddAlias(fieldName)
				if field.Multiple { // 多选
					for _, code := range rowAuth {
						db.Where(fmt.Sprintf("FIND_IN_SET(?, %s)", asFieldName), code)
					}
				}else {
					db.Where(fmt.Sprintf("%s IN ?", asFieldName), rowAuth)
				}
			}
		}
	}
	// 如果自身也是行权限模型，则进行本身数据权限过滤
	if m.isRowAuth {
		hasKv := false
		if m.config.Kvs != nil {
			if _, ok := m.config.Kvs[DefaultKvItemName]; ok{
				hasKv = true
			}
		}
		if rowAuth, isAllAuth := option.ModelAuth.GetRowAuthCallback(m.ctx, m.name); !isAllAuth {
			if hasKv{ // 存在kv，使用kv
				fieldGroup1 := ""
				fieldGroup2 := ""
				for _, fieldName := range m.config.Kvs[DefaultKvItemName].KeyFields {
					isNullFilter := false
					if InArray(fieldName, m.config.Kvs[DefaultKvItemName].NullFilterFields){
						isNullFilter = true
					}
					fieldName = strings.ReplaceAll(fieldName,"__mc_","")
					field := m.config.Fields[m.config.fieldIndexes[fieldName]]
					if field.Alias != ""{
						fieldName = field.Alias
					}else {
						fieldName = m.FieldAddAlias(fieldName)
					}
					if fieldGroup1 != "" {
						fieldGroup1 += fmt.Sprintf(",'%s',%s", m.config.Kvs[DefaultKvItemName].KeyConnector, fieldName)
					}else{
						fieldGroup1 = fieldName
					}
					if !isNullFilter {
						if fieldGroup2 != "" {
							fieldGroup2 += fmt.Sprintf(",'%s',%s", m.config.Kvs[DefaultKvItemName].KeyConnector, fieldName)
						}else{
							fieldGroup2 = fieldName
						}
					}
				}
				if m.config.Kvs[DefaultKvItemName].NullFilterFields == nil || len(m.config.Kvs[DefaultKvItemName].NullFilterFields) <=0 {
					db.Where(fmt.Sprintf("CONCAT(%s) IN ?", fieldGroup1), rowAuth)
				}else{
					db.Where(fmt.Sprintf("((CONCAT(%s) IN ?) OR (CONCAT(%s) in ? ))", fieldGroup1, fieldGroup2), rowAuth, rowAuth)
				}


			}else { // 否则使用pk字段
				db.Where(fmt.Sprintf("%s IN ?", m.FieldAddAlias(m.config.Table.Pk)), rowAuth)
			}
		}
	}
}



// 给字段加表别名
// @param fieldName 原字段名
// @return 附加另外后的字段名
func (m *Model) FieldAddAlias(fieldName string) string {
	if fieldName == "" {
		return ""
	}
	if strings.Contains(fieldName, ".") || strings.Contains(fieldName, "(") {
		return fieldName
	} else {
		return fmt.Sprintf("`%s`.`%s`", m.config.Table.Alias, strings.Trim(fieldName, " "))
	}
}

// 给字段数组加表别名
// @param fieldNames 原字段名组
// @return 附加另外后的字段名组
func (m *Model) FieldsAddAlias(fieldNames []string) []string {
	newFieldNames := make([]string, 0)
	for _, v := range fieldNames {
		if v == "" {
			continue
		}
		if strings.Contains(v, ".") || strings.Contains(v, "(") {
			newFieldNames = append(newFieldNames, v)
		} else {
			newFieldNames = append(newFieldNames, fmt.Sprintf("`%s`.`%s`", m.config.Table.Alias, strings.Trim(v, " ")))
		}
	}
	return newFieldNames
}

// 分析树形结构查询必须的扩展字段
// @return 树形结构必须的扩展字段组（层级:__mc_level、父节点id:__mc_parent 、字节点数：__mc_child_count)
func (m *Model) ParseTreeExtraField() []string {
	pathField := m.FieldAddAlias(m.config.Tree.PathField)
	mcPathField := fmt.Sprintf("`__mc_%s`.`%s`", m.config.Table.Name, m.config.Tree.PathField) //路径字段
	mcPkField := fmt.Sprintf("`__mc_%s`.`%s`", m.config.Table.Name, m.config.Table.Pk)         // PK字段

	fieldNames := make([]string, 3)
	// 层级字段
	fieldNames[0] = fmt.Sprintf("CEILING(LENGTH(%s)/%d) AS `__mc_level`", pathField, m.config.Tree.PathBit)
	// 父节点字段
	fieldNames[1] = fmt.Sprintf("(SELECT %s FROM `%s` AS `__mc_%s` WHERE %s=LEFT(%s, LENGTH(%s)-%d) LIMIT 1) AS `__mc_parent`",
		mcPkField, m.config.Table.Name, m.config.Table.Name, mcPathField, pathField, pathField, m.config.Tree.PathBit)
	// 子节点数字段
	fieldNames[2] = fmt.Sprintf("(SELECT count(%s) FROM `%s` AS `__mc_%s` WHERE %s=LEFT(%s, LENGTH(%s)-%d) LIMIT 1) AS `__mc_child_count`",
		mcPkField, m.config.Table.Name, m.config.Table.Name, pathField, mcPathField, mcPathField, m.config.Tree.PathBit)
	return fieldNames
}
